package delivery

import (
	"learning/gooop/saga/mqs/models"
	"sync"
)

type tConcurrentMsgHeap struct {
	items []*models.QueuedMsg
	size int
	mutex *sync.Mutex
}

func newMsgHeap() iMsgHeap {
	it := new(tConcurrentMsgHeap)
	it.init()
	return it
}

func (me *tConcurrentMsgHeap) init() {
	me.items = make([]*models.QueuedMsg, 0)
	me.size = 0
	me.mutex = new(sync.Mutex)
}

func (me *tConcurrentMsgHeap) Size() int {
	return me.size
}

func (me *tConcurrentMsgHeap) IsEmpty() bool {
	return me.size <= 0
}

func (me *tConcurrentMsgHeap) IsNotEmpty() bool {
	return !me.IsEmpty()
}


func (me *tConcurrentMsgHeap) has(msgID int) bool {
	if me.IsEmpty() {
		return false
	}

	for _,it := range me.items {
		if it.MsgID == msgID {
			return true
		}
	}
	return false
}

func (me *tConcurrentMsgHeap) Push(msg *models.QueuedMsg) {
	me.mutex.Lock()
	defer me.mutex.Unlock()

	if me.has(msg.MsgID) {
		return
	}

	me.ensureSize(me.size + 1)
	me.items[me.size] = msg
	me.size++

	me.shiftUp(me.size - 1)
}


func (me *tConcurrentMsgHeap) ensureSize(size int) {
	for ;len(me.items) < size; {
		me.items = append(me.items, nil)
	}
}

func (me *tConcurrentMsgHeap) parentOf(i int) int {
	return (i - 1) / 2
}

func (me *tConcurrentMsgHeap) leftChildOf(i int) int {
	return i*2 + 1
}

func (me *tConcurrentMsgHeap) rightChildOf(i int) int {
	return me.leftChildOf(i) + 1
}

func (me *tConcurrentMsgHeap) last() (i int, v *models.QueuedMsg) {
	if me.IsEmpty() {
		return -1, nil
	}

	i = me.size - 1
	v = me.items[i]
	return i,v
}

func (me *tConcurrentMsgHeap) shiftUp(i int) {
	if i <= 0 {
		return
	}
	v := me.items[i]

	pi := me.parentOf(i)
	pv := me.items[pi]

	if me.less(v, pv) {
		me.items[pi], me.items[i] = v, pv
		me.shiftUp(pi)
	}
}

func (me *tConcurrentMsgHeap) less(a, b *models.QueuedMsg) bool {
	return a.CreateTime < b.CreateTime
}

func (me *tConcurrentMsgHeap) Pop() *models.QueuedMsg {
	me.mutex.Lock()
	defer me.mutex.Unlock()

	if me.IsEmpty() {
		return nil
	}


	top := me.items[0]
	li, lv := me.last()
	me.items[0] = nil
	me.size--

	if me.IsEmpty() {
		return top
	}

	me.items[0] = lv
	me.items[li] = nil

	me.shiftDown(0)
	return top
}


func (me *tConcurrentMsgHeap) Peek() *models.QueuedMsg {
	me.mutex.Lock()
	defer me.mutex.Unlock()

	if me.IsEmpty() {
		return nil
	}

	return me.items[0]
}

func (me *tConcurrentMsgHeap) shiftDown(i int) {
	pv := me.items[i]
	ok, ci, cv := me.minChildOf(i)
	if ok && me.less(cv, pv) {
		me.items[i], me.items[ci] = cv, pv
		me.shiftDown(ci)
	}
}

func (me *tConcurrentMsgHeap) minChildOf(p int) (ok bool, i int, v *models.QueuedMsg) {
	li := me.leftChildOf(p)
	if li >= me.size {
		return false, 0, nil
	}
	lv := me.items[li]

	ri := me.rightChildOf(p)
	if ri >= me.size {
		return true, li, lv
	}
	rv := me.items[ri]

	if me.less(lv, rv) {
		return true, li, lv
	} else {
		return true, ri, rv
	}
}
